﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace TicTacToe
{
    public enum MinMaxType
    {
        Minimizer,
        Maximizer
    } ;

    public class MinMax
    {
        private int _currentSearchLevel;
        private List<MinMax> _childNodes;
        public GameBoard.SquareStatus[,] _currentState { get; private set; }
        private MinMaxType _type;
        public double Heuristic { get; private set; }

        public MinMax(GameBoard.SquareStatus[,] currentState, int currentSearchLevel, MinMaxType type)
        {
            _currentState = currentState;
            _currentSearchLevel = currentSearchLevel;
            
            _type = type;
            Heuristic = (_type == MinMaxType.Minimizer ? Double.PositiveInfinity : Double.NegativeInfinity);
        }

        public double Search(double alpha, double beta)
        {
            if (GameBoard.CheckIfCrossWon(_currentState))
            {
                Heuristic = 1000*_currentSearchLevel;
                return Heuristic;
            }

            if (GameBoard.CheckIfCircleWon(_currentState))
            {
                Heuristic = -1000*_currentSearchLevel;
                return Heuristic;
            }

            if (GameBoard.CheckForDraw(_currentState))
            {
                Heuristic = 0.0;
                return Heuristic;
            }

            if (_currentSearchLevel == 0)
            {
                Heuristic = CalculateStaticEvaluation();
                return Heuristic;
            }

            if (_childNodes == null)
                PopulateChildNodes();

            foreach (var child in _childNodes)
            {
                double V;

                if (alpha < beta)
                {
                    V = child.Search(alpha, beta);
                    if (_type == MinMaxType.Minimizer)
                    {
                        if (V < beta)
                        {
                            beta = Heuristic = V;
                        }
                    }
                    else
                    {
                        if (V > alpha)
                        {
                            alpha = Heuristic = V;
                        }
                    }
                }
                else
                    break;
            }

            return Heuristic;
        }

        private double CalculateStaticEvaluation()
        {
            double h = 0;
            for(int i = 0; i < _currentState.GetLength(0); i++)
                for (int j = 0; j < _currentState.GetLength(1); j++)
                {
                    if (_currentState[i, j] == (_type == MinMaxType.Minimizer ? GameBoard.SquareStatus.Circle : GameBoard.SquareStatus.Cross) 
                        || _currentState[i, j] == (_type == MinMaxType.Minimizer ? GameBoard.SquareStatus.CircleSelected : GameBoard.SquareStatus.CrossSelected))
                        h += GameBoard.StaticEvaluation[i][j];
                }

            return h;
        }

        public void PopulateChildNodes()
        {
            _childNodes = new List<MinMax>();
            for(int i = 0; i < _currentState.GetLength(0); i++)
                for (int j = 0; j < _currentState.GetLength(1); j++) {
                    if (_currentState[i, j] == GameBoard.SquareStatus.Empty || _currentState[i, j] == GameBoard.SquareStatus.EmptySelected)
                    {
                        var newState = _currentState.Clone() as GameBoard.SquareStatus[,];                       
                        newState[i, j] = (_type == MinMaxType.Minimizer? GameBoard.SquareStatus.Circle : GameBoard.SquareStatus.Cross);
                        _childNodes.Add(new MinMax(newState, _currentSearchLevel - 1, (_type == MinMaxType.Minimizer ? MinMaxType.Maximizer : MinMaxType.Minimizer)));
                    }
                }
        }

        public GameBoard.SquareStatus[,] NextMove()
        {
            var _heuristic = Search(double.NegativeInfinity, double.PositiveInfinity);
            return _childNodes.Where(e => e.Heuristic ==_heuristic).First()._currentState;
        }
    }
}